package com.kkb.kkbportal.service.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kkb.kkbcommon.exception.Asserts;
import com.kkb.kkbportal.dao.ScoreDao;
import com.kkb.kkbportal.domain.ProjectInfo;
import com.kkb.kkbportal.domain.Score;
import com.kkb.kkbportal.domain.dto.ScoreDto;
import com.kkb.kkbportal.domain.ums.Role;
import com.kkb.kkbportal.domain.ums.User;
import com.kkb.kkbportal.domain.ums.UserRole;
import com.kkb.kkbportal.domain.vo.DetailAvgStarVo;
import com.kkb.kkbportal.domain.vo.DetailScoreVo;
import com.kkb.kkbportal.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author qtds
 * @since 2021-08-12
 */
@Service
public class ScoreServiceImpl extends ServiceImpl<ScoreDao, Score> implements ScoreService {

    private static final double AVG_STAR_DOUBLE_NUMBER = 3.0;
    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private UserRoleService userRoleService;


    @Autowired
    private ProjectManagementService projectManagementService;


    @Override
    public void updateByMasterIdAndSlaveIdAndObjectId(ScoreDto scoreDto) {
        Score score = formatScoreDtoToScore(scoreDto);
        boolean update = this.lambdaUpdate()
                .eq(Score::getMasterId, score.getMasterId())
                .eq(Score::getSlaveId, score.getSlaveId())
                .eq(Score::getObjectId, score.getObjectId())
                .update(score);

        if (!update) {
            Asserts.fail("更新失败");
        }
    }

    @Override
    public void updateById(Long id, ScoreDto scoreDto) {
        scoreDto.setId(id);
        Score score = formatScoreDtoToScore(scoreDto);
        boolean update = updateById(score);

        if (!update) {
            Asserts.fail("插入失败");
        }
    }

    @Override
    public void addScore(ScoreDto scoreDto) {
        Score score = formatScoreDtoToScore(scoreDto);
        boolean save = save(score);
        if (!save) {
            Asserts.fail("添加失败");
        }
    }

    @Override
    public List<Score> listAll() {
        List<Score> list = list();
        if (ObjectUtil.isNull(list)) {
            Asserts.fail("未查到评分信息");
        }
        return list;
    }

    @Override
    public void deleteById(Long id) {
        boolean remove = removeById(id);
        if (!remove) {
            Asserts.fail("删除失败");
        }
    }

    @Override
    public BigDecimal countAvgSlaveStar(Long slaveId) {
        List<Score> list = this.lambdaQuery()
                .eq(Score::getSlaveId, slaveId)
                .list();

        int sum = 0;
        for (Score score : list) {
            sum += score.getStarOne();
            sum += score.getStarTwo();
            sum += score.getStarThree();
        }
        double star = sum / (list.size() * AVG_STAR_DOUBLE_NUMBER);
        BigDecimal bigDecimal = BigDecimal.valueOf(star);

        return bigDecimal.setScale(1, BigDecimal.ROUND_HALF_UP);
    }

    @Override
    public BigDecimal countAvgSlaveStar(Long slaveId, List<Long> masterIds) {

        List<Score> list = this.lambdaQuery()
                .eq(Score::getSlaveId, slaveId)
                .list();

        if (ObjectUtil.isEmpty(list)) {
            Asserts.fail("未找到被评人数据");
        }

        if (ObjectUtil.isEmpty(masterIds)) {
            return BigDecimal.ZERO;
        }

        Set<Long> masterIdSet = new HashSet<>(masterIds);
        int sum = 0;
        for (Score score : list) {
            if (masterIdSet.contains(score.getMasterId())) {
                sum += score.getStarOne();
                sum += score.getStarTwo();
                sum += score.getStarThree();
            }
        }
        double star = sum / (masterIdSet.size() * AVG_STAR_DOUBLE_NUMBER);
        BigDecimal bigDecimal = BigDecimal.valueOf(star);
        return bigDecimal.setScale(1, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 根据被评价人id计算综合得分列表（group by 评价人角色）
     *
     * @param slaveId 被评价人id
     * @return 平均分列表信息List<DetailAvgStarVo>
     * @Author qtds(chenYong)
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<DetailAvgStarVo> avgStarListBySlaveId(Long slaveId) {

        List<Score> scoreList = this.lambdaQuery()
                .eq(Score::getSlaveId, slaveId)
                .list();

        if (CollUtil.isEmpty(scoreList)) {
            Asserts.fail("未找到有关的评价信息");
        }
        // 转换成主键映射关系
        Map<Long, Score> scoreMap = scoreList.stream()
                .collect(Collectors.toMap(Score::getId, score -> score));

        // 根据  user角色id (map KeySet) 集合得到 List<UserRole>
        List<UserRole> userRoleList = userRoleService.listByIds(scoreMap.keySet());

        if (CollUtil.isEmpty(userRoleList)) {
            Asserts.fail("未找到评价人的角色信息");
        }

        // 得到roleId 和 对应用户id的映射关系(group by 角色id)
        Map<Long, List<Long>> roleToUserListMap = userRoleList.stream()
                .collect(Collectors.groupingBy(UserRole::getRoleId,
                        Collectors.mapping(UserRole::getUserId, Collectors.toList())));

        // 根据ids（分组后的角色id set集合）一次性找到所有相关的角色信息列表
        List<Role> roles = roleService.listByIds(roleToUserListMap.keySet());

        if (CollUtil.isEmpty(roles)) {
            Asserts.fail("未找到角色相关信息");
        }

        // 转换成id->角色role 主键映射关系
        Map<Long, Role> roleMap = roles.stream().collect(Collectors.toMap(Role::getId, role -> role));

        // 平均分列表信息DetailAvgStarVoList
        List<DetailAvgStarVo> list = new ArrayList<>();

        // 遍历角色与用户映射roleToUserMap，找出数据
        for (Long roleId : roleToUserListMap.keySet()) {
            DetailAvgStarVo vo = new DetailAvgStarVo();

            Role role = roleMap.get(roleId);
            vo.setMasterRoleId(role.getId());
            vo.setMasterName(role.getName());

            // 得到对应角色下的用户idList
            List<Long> userIdList = roleToUserListMap.get(roleId);

            // 为空直接设为0，进入下一个角色信息遍历
            if (CollUtil.isEmpty(userIdList)) {
                vo.setSlaveAvgStar(BigDecimal.ZERO);
                list.add(vo);
            }
            // 定义平均分star
            double avgStar = 0.0;
            // 遍历list来计算对应角色的评分
            for (Long userId : userIdList) {
                // 得到评价人的评分信息
                Score score = scoreMap.get(userId);
                avgStar += (score.getStarOne() + score.getStarTwo() + score.getStarThree()) / AVG_STAR_DOUBLE_NUMBER;
            }

            avgStar /= userIdList.size();
            vo.setSlaveAvgStar(BigDecimal.valueOf(avgStar).setScale(1, BigDecimal.ROUND_HALF_UP));
            list.add(vo);
        }
        return list;
    }

    /**
     * 根据被评价人id，查找所获得的详细评价信息列表
     *
     * @param slaveId 被评价人id
     * @return 评价列表
     * @author syt
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<DetailScoreVo> selectScoreDetailListBySlaveId(Long slaveId) {

        // 1. 根据被评价人id，查找评价id列表
        List<Score> scoreList = this.lambdaQuery()
                .eq(Score::getSlaveId, slaveId)
                .list();

        if (CollUtil.isEmpty(scoreList)){
            Asserts.fail("未找到评价列表！");
        }

        List<DetailScoreVo> detailScoreVoList = new ArrayList<>();

        for (Score score : scoreList) {
            DetailScoreVo detailScoreVo = new DetailScoreVo();

            // a. 根据评价id可获得的信息：评价时间、评价内容、综合星级
            Long scoreId = score.getId();
            detailScoreVo.setId(scoreId);
            detailScoreVo.setCommentTime(score.getUpdateTime());
            detailScoreVo.setMsg(score.getMsg());

            double star = (score.getStarOne() + score.getStarTwo() + score.getStarThree()) / AVG_STAR_DOUBLE_NUMBER;
            BigDecimal avgStar = BigDecimal.valueOf(star).setScale(1, BigDecimal.ROUND_HALF_UP);
            detailScoreVo.setAverageStar(avgStar);

            // b. 根据被评价人id可获得的信息：评价人头像、姓名、角色
            Long masterId = score.getMasterId();
            User master = userService.getById(masterId);
            // b1 判断评论是否为匿名类型 0:展示匿名信息 1:展示真实信息
            if(!score.getScoreIsReal()){
                // Todo: group-2
                detailScoreVo.setMasterAvatar("匿名头像url");
                detailScoreVo.setMasterName("匿名");
            }else{
                detailScoreVo.setMasterAvatar(master.getHeadUrl());
                detailScoreVo.setMasterName(master.getName());
            }

            // b2 查找 评价人的角色id列表
            String masterRoleName = null;
            List<UserRole> masterRoleList = userRoleService.lambdaQuery().eq(UserRole::getUserId, masterId).list();

            if(CollUtil.isEmpty(masterRoleList)){
                Asserts.fail("用户："+master.getName() + "，没有角色信息");
            }

            // b3 查找 导师、学员、甲方的角色id，并存入List
            List<Long> ids = new ArrayList<>();
            Long tutorRoleId = roleService.lambdaQuery().eq(Role::getName, "导师").one().getId();
            Long studentRoleId = roleService.lambdaQuery().eq(Role::getName, "学员").one().getId();
            Long firstPartyRoleId = roleService.lambdaQuery().eq(Role::getName, "甲方").one().getId();
            ids.add(tutorRoleId);
            ids.add(studentRoleId);
            ids.add(firstPartyRoleId);

            for (UserRole masterRole : masterRoleList) {
                Long masterRoleId = masterRole.getRoleId();
                // b4 判断 评价人的角色是否为导师、学员、甲方
                if (ids.contains(masterRoleId)) {
                    masterRoleName = roleService.lambdaQuery().eq(Role::getId, masterRoleId).one().getName();
                    break;
                }
            }

            detailScoreVo.setMasterRoleName(masterRoleName);

            // c. 根据项目id，获得被评价的项目类型
            Long objectId = score.getObjectId();
            ProjectInfo projectInfo = projectManagementService.getById(objectId);
            detailScoreVo.setProjectType(projectInfo.getType());

            detailScoreVoList.add(detailScoreVo);
        }

        return detailScoreVoList;
    }

    private Score formatScoreDtoToScore(ScoreDto scoreDto) {
        return Score.builder()
                .id(scoreDto.getId())
                .masterId(scoreDto.getMasterId())
                .slaveId(scoreDto.getSlaveId())
                .objectId(scoreDto.getObjectId())
                .msg(scoreDto.getMsg())
                .starOne(scoreDto.getStarOne())
                .starTwo(scoreDto.getStarTwo())
                .starThree(scoreDto.getStarThree())
                // 默认为实名
                .scoreIsReal(scoreDto.getScoreIsReal() == null || scoreDto.getScoreIsReal())
                .build();
    }
}
